/************************************************************************
* events.c
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "common.h"
#define _WM_EXPOSE_ALL
#include "wm.h"

#include <stdio.h>
#include <string.h>
/*
typedef struct bind_s {
	char str[256];
	sym_t sym;
	uint8_t mods;
} bind_t;
*/

static uint32_t kmap_strtosym(char* str, sym_t *sym)
{
	sym->ch = 0;
	if (utf8_strlen(str) == 1) {
		sym->type = SYM_TYPE_KEY;
		sym->sym = utf8_toutf32(str,-1);
		sym->ch = sym->sym;
		return 0;
	}else if (!strcmp(str,"space")) {
		sym->type = SYM_TYPE_SKEY;
		sym->sym = SYM_KEY_SPACE;
		sym->ch = ' ';
		return 0;
	}else if (!strcmp(str,"ctrl")) {
		sym->type = SYM_TYPE_MOD;
		sym->sym = SYM_MOD_CTRL;
		return 0;
	}else if (!strcmp(str,"alt")) {
		sym->type = SYM_TYPE_MOD;
		sym->sym = SYM_MOD_ALT;
		return 0;
	}else if (!strcmp(str,"shift")) {
		sym->type = SYM_TYPE_MOD;
		sym->sym = SYM_MOD_SHIFT;
		return 0;
	}else if (!strcmp(str,"super")) {
		sym->type = SYM_TYPE_MOD;
		sym->sym = SYM_MOD_SUPER;
		return 0;
	}else if (!strcmp(str,"mouse-left")) {
		sym->type = SYM_TYPE_MOUSE;
		sym->sym = MOUSE_BUTTON_LEFT;
		return 0;
	}else if (!strcmp(str,"mouse-centre")) {
		sym->type = SYM_TYPE_MOUSE;
		sym->sym = MOUSE_BUTTON_RIGHT;
		return 0;
	}else if (!strcmp(str,"mouse-right")) {
		sym->type = SYM_TYPE_MOUSE;
		sym->sym = MOUSE_BUTTON_LEFT;
		return 0;
	}else if (!strcmp(str,"scroll-up")) {
		sym->type = SYM_TYPE_MOUSE;
		sym->sym = MOUSE_BUTTON_UP;
		return 0;
	}else if (!strcmp(str,"scroll-down")) {
		sym->type = SYM_TYPE_MOUSE;
		sym->sym = MOUSE_BUTTON_DOWN;
		return 0;
	}else if (!strcmp(str,"mouse-motion")) {
		sym->type = SYM_TYPE_MOUSE;
		sym->sym = MOUSE_MOTION;
		return 0;
	}else if (!strcmp(str,"escape")) {
		sym->type = SYM_TYPE_SKEY;
		sym->sym = SYM_KEY_ESCAPE;
		return 0;
	}else if (!strcmp(str,"tab")) {
		sym->type = SYM_TYPE_SKEY;
		sym->sym = SYM_KEY_TAB;
		sym->ch = '\n';
		return 0;
	}else if (!strcmp(str,"up")) {
		sym->type = SYM_TYPE_SKEY;
		sym->sym = SYM_KEY_UP;
		return 0;
	}else if (!strcmp(str,"down")) {
		sym->type = SYM_TYPE_SKEY;
		sym->sym = SYM_KEY_DOWN;
		return 0;
	}else if (!strcmp(str,"right")) {
		sym->type = SYM_TYPE_SKEY;
		sym->sym = SYM_KEY_RIGHT;
		return 0;
	}else if (!strcmp(str,"left")) {
		sym->type = SYM_TYPE_SKEY;
		sym->sym = SYM_KEY_LEFT;
		return 0;
	}else if (!strncmp(str,"kp",2)) {
		sym->type = SYM_TYPE_SKEY;
		sym->ch = str[2];
		switch (str[2]) {
		case '0':
			sym->sym = SYM_KEY_KP0;
			break;
		case '1':
			sym->sym = SYM_KEY_KP1;
			break;
		case '2':
			sym->sym = SYM_KEY_KP2;
			break;
		case '3':
			sym->sym = SYM_KEY_KP3;
			break;
		case '4':
			sym->sym = SYM_KEY_KP4;
			break;
		case '5':
			sym->sym = SYM_KEY_KP5;
			break;
		case '6':
			sym->sym = SYM_KEY_KP6;
			break;
		case '7':
			sym->sym = SYM_KEY_KP7;
			break;
		case '8':
			sym->sym = SYM_KEY_KP8;
			break;
		case '9':
			sym->sym = SYM_KEY_KP9;
			break;
		default:
			sym->ch = 0;
		}
	}else if (str[0] == 'f' || str[0] == 'F') {
		int i = strtol(str+1,NULL,10);
		if (i < 1 || i > 12)
			return 1;
		i--;
		sym->type = SYM_TYPE_SKEY;
		sym->sym = SYM_KEY_F1+i;
	}
	return 1;
}

/* convert a string to sym + modifiers */
int kmap_strtobind(bind_t *bind, char* str)
{
	char* b;
	char* n;
	char* e;
	sym_t s;
	if (!str || !str[0])
		str = "???";
	if (snprintf(bind->str,256,"%s",str) >= 256)
		return 1;

	bind->mods = 0;
	bind->sym.type = SYM_TYPE_NONE;
	bind->sym.sym = 0;
	bind->sym.ch = 0;

	/* special unbound case */
	if (!strcmp(str,"???"))
		return 0;

	/* single utf8 char is simple */
	if (utf8_strlen(str) == 1) {
		bind->sym.type = SYM_TYPE_KEY;
		bind->sym.sym = utf8_toutf32(str,-1);
		return 0;
	}


	n = bind->str;
	e = utf8_strchr(bind->str,'+',NULL);

	if (!e) {
		if (kmap_strtosym(str,&bind->sym)) {
			strcpy(bind->str,"???");
			return 1;
		}
		return 0;
	}

	while (n && n[0]) {
		b = n;
		e = utf8_strchr(b,'+',NULL);
		if (e) {
			/* odd case '+' key */
			if (b == e) {
				e++;
				if (*e) {
					n = e+1;
					*e = 0;
				}else{
					e = NULL;
					n = NULL;
				}
			}else{
				n = e+1;
				*e = 0;
			}
		}else{
			n = NULL;
		}
		if (kmap_strtosym(b,&s)) {
			strcpy(bind->str,"???");
			bind->mods = 0;
			bind->sym.type = SYM_TYPE_NONE;
			bind->sym.sym = 0;
			bind->sym.ch = 0;
			return 1;
		}

		switch (s.type) {
		case SYM_TYPE_KEY:
		case SYM_TYPE_SKEY:
		case SYM_TYPE_MOUSE:
			bind->sym.type = s.type;
			bind->sym.sym = s.sym;
			break;
		case SYM_TYPE_MOD:
			bind->mods |= s.sym;
			break;
		default:
			strcpy(bind->str,"???");
			bind->mods = 0;
			bind->sym.type = SYM_TYPE_NONE;
			bind->sym.sym = 0;
			bind->sym.ch = 0;
			return 1;
		}

		if (e)
			*e = '+';

		if (bind->sym.type != SYM_TYPE_NONE)
			break;
	}

	return 0;
}

int kmap_bindtostr(char* str, int size, bind_t *bind)
{
	int a = 0;
	char buff[6];
	/* special unbound case */
	if (bind->sym.type == SYM_TYPE_NONE && bind->mods == 0) {
		strcpy(str,"???");
		return 0;
	}

	str[0] = 0;

	if ((bind->mods&SYM_MOD_CTRL) == SYM_MOD_CTRL) {
		if (a)
			strappend(str,size,"+");
		strappend(str,size,"ctrl");
	}
	if ((bind->mods&SYM_MOD_ALT) == SYM_MOD_ALT) {
		if (a)
			strappend(str,size,"+");
		strappend(str,size,"alt");
	}
	if ((bind->mods&SYM_MOD_SHIFT) == SYM_MOD_SHIFT) {
		if (a)
			strappend(str,size,"+");
		strappend(str,size,"shift");
	}
	if ((bind->mods&SYM_MOD_SUPER) == SYM_MOD_SUPER) {
		if (a)
			strappend(str,size,"+");
		strappend(str,size,"super");
	}

	switch (bind->sym.type) {
	case SYM_TYPE_KEY:
		if (!utf8_fromutf32(buff,6,bind->sym.ch))
			return 1;
		if (a)
			strappend(str,size,"+");
		strappend(str,size,buff);
		break;
	case SYM_TYPE_MOUSE:
		switch (bind->sym.sym) {
		case MOUSE_BUTTON_LEFT:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"mouse-left");
			break;
		case MOUSE_BUTTON_CENTRE:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"mouse-centre");
			break;
		case MOUSE_BUTTON_RIGHT:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"mouse-right");
			break;
		case MOUSE_BUTTON_UP:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"scroll-up");
			break;
		case MOUSE_BUTTON_DOWN:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"scroll-down");
			break;
		case MOUSE_MOTION:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"mouse-motion");
			break;
		default:
			return 1;
		}
		break;
	case SYM_TYPE_SKEY:
		switch (bind->sym.sym) {
		case SYM_KEY_SPACE:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"space");
			break;
		case SYM_KEY_ESCAPE:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"escape");
			break;
		case SYM_KEY_TAB:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"tab");
			break;
		case SYM_KEY_KP0:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp0");
			break;
		case SYM_KEY_KP1:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp1");
			break;
		case SYM_KEY_KP2:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp2");
			break;
		case SYM_KEY_KP3:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp3");
			break;
		case SYM_KEY_KP4:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp4");
			break;
		case SYM_KEY_KP5:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp5");
			break;
		case SYM_KEY_KP6:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp6");
			break;
		case SYM_KEY_KP7:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp7");
			break;
		case SYM_KEY_KP8:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp8");
			break;
		case SYM_KEY_KP9:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"kp9");
			break;
		case SYM_KEY_UP:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"up");
			break;
		case SYM_KEY_DOWN:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"down");
			break;
		case SYM_KEY_LEFT:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"left");
			break;
		case SYM_KEY_RIGHT:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"right");
			break;
		case SYM_KEY_F1:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f1");
			break;
		case SYM_KEY_F2:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f2");
			break;
		case SYM_KEY_F3:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f3");
			break;
		case SYM_KEY_F4:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f4");
			break;
		case SYM_KEY_F5:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f5");
			break;
		case SYM_KEY_F6:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f6");
			break;
		case SYM_KEY_F7:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f7");
			break;
		case SYM_KEY_F8:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f8");
			break;
		case SYM_KEY_F9:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f9");
			break;
		case SYM_KEY_F10:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f10");
			break;
		case SYM_KEY_F11:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f11");
			break;
		case SYM_KEY_F12:
			if (a)
				strappend(str,size,"+");
			strappend(str,size,"f12");
			break;
		default:
			return 1;
		}
		break;
	default:;
	}

	if (str[0] == 0)
		return 1;

	return 0;
}

/* check if two bindings match */
int kmap_equal(bind_t *b1, bind_t *b2)
{
	if (!b1 || !b2)
		return 0;
	if (b1->sym.type != b2->sym.type)
		return 0;
	if (b1->sym.sym != b2->sym.sym)
		return 0;
	if (b1->mods != b2->mods)
		return 0;
	return 1;
}

/* does the event binding trigger the action binding? */
int kmap_triggers(bind_t *eb, bind_t *ab)
{
	if (!eb || !ab)
		return 0;
	if (eb->sym.type != ab->sym.type)
		return 0;
	if (eb->sym.sym != ab->sym.sym)
		return 0;
	if ((eb->mods&ab->mods) != ab->mods)
		return 0;
	return 1;
}
